package twilight.companion.entity;

import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.TamableAnimal;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.*;
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.OwnerHurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.OwnerHurtTargetGoal;
import net.minecraft.world.entity.monster.RangedAttackMob;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.event.ForgeEventFactory;
import org.jetbrains.annotations.Nullable;
import twilight.companion.entity.ai.MiniYetiRampageGoal;
import twilight.companion.entity.ai.MiniYetiThrowRiderGoal;
import twilight.companion.entity.ai.MiniYetiCrouchGoal;
import twilight.companion.entity.ai.MiniYetiSeekWaterGoal;
import twilight.companion.entity.ai.MiniYetiHighPriorityThrowGoal;
import twilight.companion.entity.ai.MiniYetiOwnerAttackTargetGoal;

import twilightforest.entity.projectile.FallingIce;
import twilightforest.entity.projectile.IceBomb;
import twilightforest.init.TFEntities;
import twilightforest.init.TFParticleType;
import twilightforest.init.TFSounds;
import twilightforest.util.EntityUtil;
import twilightforest.util.WorldUtil;

public class MiniYetiEntity extends TamableAnimal implements RangedAttackMob {
    private static final EntityDataAccessor<Byte> RAMPAGE_FLAG = SynchedEntityData.defineId(MiniYetiEntity.class,
            EntityDataSerializers.BYTE);

    private static final EntityDataAccessor<Byte> GIANT_FLAG = SynchedEntityData.defineId(MiniYetiEntity.class,
            EntityDataSerializers.BYTE);
    private static final EntityDataAccessor<Byte> CROUCHING_FLAG = SynchedEntityData.defineId(MiniYetiEntity.class,
            EntityDataSerializers.BYTE);

    private static final EntityDataAccessor<Float> GROWTH_SCALE = SynchedEntityData.defineId(MiniYetiEntity.class,
            EntityDataSerializers.FLOAT);
    public static final EntityDataAccessor<Boolean> CAPTURE_ANIMATION = SynchedEntityData.defineId(MiniYetiEntity.class,
            EntityDataSerializers.BOOLEAN);
    public static final EntityDataAccessor<Integer> CAPTURE_TIMER = SynchedEntityData.defineId(MiniYetiEntity.class,
            EntityDataSerializers.INT);
    public static final EntityDataAccessor<Boolean> SUMMON_ANIMATION = SynchedEntityData.defineId(MiniYetiEntity.class,
            EntityDataSerializers.BOOLEAN);
    public static final EntityDataAccessor<Integer> SUMMON_TIMER = SynchedEntityData.defineId(MiniYetiEntity.class,
            EntityDataSerializers.INT);

    private int collisionCounter;
    private boolean canRampage;

    private float targetScale = 0.3F;
    private int growthTicks = 0;

    public MiniYetiEntity(EntityType<? extends TamableAnimal> entityType, Level level) {
        super(entityType, level);
    }

    @Override
    protected void registerGoals() {
        this.goalSelector.addGoal(0, new FloatGoal(this));
        this.goalSelector.addGoal(1, new MiniYetiSeekWaterGoal(this, 1.5D));
        this.goalSelector.addGoal(2, new MiniYetiHighPriorityThrowGoal(this, 5.0D));
        this.goalSelector.addGoal(3, new MiniYetiCrouchGoal(this));

        this.goalSelector.addGoal(4, new MiniYetiRampageGoal(this, 6, 60) {
            @Override
            public boolean canUse() {
                return MiniYetiEntity.this.getTarget() != null &&
                        MiniYetiEntity.this.distanceToSqr(MiniYetiEntity.this.getTarget()) <= 36.0D &&
                        super.canUse();
            }

            @Override
            public boolean canContinueToUse() {
                return super.canContinueToUse() && MiniYetiEntity.this.getTarget() != null;
            }
        });

        this.goalSelector.addGoal(5, new RangedAttackGoal(this, 1.0D, 25, 25, 20.0F) {
            @Override
            public boolean canUse() {
                return MiniYetiEntity.this.getTarget() != null &&
                        !MiniYetiEntity.this.isRampaging() &&
                        MiniYetiEntity.this.distanceToSqr(MiniYetiEntity.this.getTarget()) >= 49D &&
                        MiniYetiEntity.this.distanceToSqr(MiniYetiEntity.this.getTarget()) <= 400D &&
                        super.canUse();
            }
        });

        this.goalSelector.addGoal(6, new MiniYetiThrowRiderGoal(this, 1.0D, false) {
            @Override
            public boolean canUse() {
                return MiniYetiEntity.this.getTarget() != null &&
                        (MiniYetiEntity.this.isRampaging() ||
                        MiniYetiEntity.this.distanceToSqr(MiniYetiEntity.this.getTarget()) <= 36.0D) && 
                        super.canUse();
            }
        });
        this.goalSelector.addGoal(7, new LeapAtTargetGoal(this, 0.4F));
        this.goalSelector.addGoal(8, new MeleeAttackGoal(this, 1.0D, true));
        this.goalSelector.addGoal(9, new FollowOwnerGoal(this, 1.0D, 10.0F, 2.0F, false));
        this.goalSelector.addGoal(10, new WaterAvoidingRandomStrollGoal(this, 1.0D));
        this.goalSelector.addGoal(11, new LookAtPlayerGoal(this, Player.class, 8.0F));
        this.goalSelector.addGoal(12, new RandomLookAroundGoal(this));
        this.targetSelector.addGoal(0, new MiniYetiOwnerAttackTargetGoal(this));
        this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this) {

            @Override
            public boolean canUse() {
                return super.canUse() && MiniYetiEntity.this.getOwner() != null;
            }
        });
        this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this) {
            @Override
            public boolean canUse() {
                return super.canUse() && MiniYetiEntity.this.getOwner() != null;
            }
        });
        this.targetSelector.addGoal(3, new HurtByTargetGoal(this) {
            @Override
            protected boolean canAttack(LivingEntity potentialTarget,
            net.minecraft.world.entity.ai.targeting.TargetingConditions targetPredicate) {
                return potentialTarget != MiniYetiEntity.this.getOwner() && 
                super.canAttack(potentialTarget, targetPredicate);
            }
        }.setAlertOthers(MiniYetiEntity.class));
    }

    @Override
    protected void defineSynchedData() {
        super.defineSynchedData();
        this.getEntityData().define(RAMPAGE_FLAG, (byte) 0);
        this.getEntityData().define(GIANT_FLAG, (byte) 0);
        this.getEntityData().define(CROUCHING_FLAG, (byte) 0);
        this.getEntityData().define(GROWTH_SCALE, 0.3F);
        this.getEntityData().define(CAPTURE_ANIMATION, false);
        this.getEntityData().define(CAPTURE_TIMER, 0);
        this.getEntityData().define(SUMMON_ANIMATION, false);
        this.getEntityData().define(SUMMON_TIMER, 0);
    }

    @Override
    public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
        super.onSyncedDataUpdated(key);
        if (GIANT_FLAG.equals(key) || GROWTH_SCALE.equals(key)) {
            this.refreshDimensions();
            this.setBoundingBox(this.makeBoundingBox());
        }
    }

    public static AttributeSupplier.Builder createAttributes() {
        return TamableAnimal.createLivingAttributes()
        .add(Attributes.MAX_HEALTH, 160.0D)
        .add(Attributes.MOVEMENT_SPEED, 0.30D)
        .add(Attributes.ATTACK_DAMAGE, 12.0D)
        .add(Attributes.ATTACK_KNOCKBACK, 0.6D)
        .add(Attributes.FOLLOW_RANGE, 32.0D)
        .add(Attributes.KNOCKBACK_RESISTANCE, 0.4D);
    }

    @Override
    public boolean isFood(ItemStack stack) {
        return false;
    }

    @Nullable
    @Override
    public TamableAnimal getBreedOffspring(net.minecraft.server.level.ServerLevel level,
        net.minecraft.world.entity.AgeableMob otherParent) {
        return null;
    }

    @Override
    public void aiStep() {
        if (!this.getPassengers().isEmpty() && this.getPassengers().get(0).isShiftKeyDown()) {
            this.getPassengers().get(0).setShiftKeyDown(false);
        }

        super.aiStep();

        if (this.isVehicle()) {
            this.getLookControl().setLookAt(getPassengers().get(0), 100F, 100F);
        }

        if (this.getEntityData().get(SUMMON_ANIMATION)) {
            this.getNavigation().stop();
            this.setTarget(null);

            int timer = this.getEntityData().get(SUMMON_TIMER);

            if (!this.level().isClientSide()) {
                timer++;
                this.getEntityData().set(SUMMON_TIMER, timer);

                if (timer >= 60) {
                    this.getEntityData().set(SUMMON_ANIMATION, false);
                    this.getEntityData().set(SUMMON_TIMER, 0);
                    float finalScale = this.isGiant() ? 1.2F : 0.3F;
                    this.setGrowthScale(finalScale);
                    return;
                }

                float growthProgress = Math.min(timer / 60.0F, 1.0F);
                float targetScale = this.isGiant() ? 1.2F : 0.3F;
                float newScale = 0.01F + (targetScale - 0.01F) * growthProgress;

                this.setGrowthScale(newScale);
            }

            if (this.level().isClientSide() && timer % 2 == 0) {
                for (int i = 0; i < 8; i++) {
                    double angle = (i / 8.0) * 2 * Math.PI;
                    double radius = 1.0 + Math.sin(timer * 0.1) * 0.5;
                    double x = this.getX() + Math.cos(angle) * radius;
                    double z = this.getZ() + Math.sin(angle) * radius;
                    double y = this.getY() + this.random.nextDouble() * 2.0D;

                    this.level().addParticle(ParticleTypes.SNOWFLAKE, x, y, z, 0.0D, 0.05D, 0.0D);

                    if (i % 2 == 0) {
                        this.level().addParticle(TFParticleType.SNOW.get(), x, y, z, 0.0D, 0.02D, 0.0D);
                    }
                }
            }
        }

        if (this.getEntityData().get(CAPTURE_ANIMATION)) {
            if (!this.level().isClientSide()) {
                int timer = this.getEntityData().get(CAPTURE_TIMER);
                timer++;
                this.getEntityData().set(CAPTURE_TIMER, timer);
                if (timer >= 60) {
                    this.remove(Entity.RemovalReason.DISCARDED);
                    return;
                }
            }

            float rotationSpeed = 15.0F;
            this.setYRot(this.getYRot() + rotationSpeed);
            this.yBodyRot = this.getYRot();
            this.yHeadRot = this.getYRot();
            this.setXRot(0);
            this.setDeltaMovement(0, 0.15, 0);

            int timer = this.getEntityData().get(CAPTURE_TIMER);
            float shrinkProgress = timer / 60.0F;

            float initialScale = this.isGiant() ? 1.2F : 0.3F;
            float newScale = initialScale * (1.0F - shrinkProgress);

            if (newScale > 0.01F) {
                this.setGrowthScale(newScale);
            } else {
                this.setGrowthScale(0.01F);
            }

            if (this.level().isClientSide() && timer % 3 == 0) {
                for (int i = 0; i < 5; i++) {
                    this.level().addParticle(ParticleTypes.SNOWFLAKE,
                    this.getX() + (this.random.nextDouble() - 0.5D) * 1.5D,
                    this.getY() + this.random.nextDouble() * 1.5D,
                    this.getZ() + (this.random.nextDouble() - 0.5D) * 1.5D,
                    0.0D, 0.1D, 0.0D);
                }
            }
        }

        if (!this.level().isClientSide()) {
            if (this.growthTicks > 0 && !this.isSummoning()) {
                this.growthTicks--;
                float currentScale = this.getGrowthScale();
                float progress = (100 - this.growthTicks) / 100.0F;
                float newScale = 0.3F + (this.targetScale - 0.3F) * progress;
                this.setGrowthScale(newScale);

                if (this.growthTicks <= 0) {
                    this.setGrowthScale(this.targetScale);
                    if (this.targetScale >= 1.2F) {
                        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(400.0D);
                        this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(30.0D);
                        this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.25D);
                        this.getAttribute(Attributes.KNOCKBACK_RESISTANCE).setBaseValue(0.8D);
                        this.setHealth(this.getMaxHealth());
                    }
                }

                if (this.tickCount % 5 == 0) {
                    for (int i = 0; i < 3; i++) {
                        this.level().addParticle(ParticleTypes.SNOWFLAKE,
                        this.getX() + (this.random.nextDouble() - 0.5D) * 2.0D,
                        this.getY() + this.random.nextDouble() * 2.0D,
                        this.getZ() + (this.random.nextDouble() - 0.5D) * 2.0D,
                        0.0D, 0.05D, 0.0D);
                    }
                }
            }

            if (this.isRampaging() && (this.horizontalCollision || this.verticalCollision)) {
                this.collisionCounter++;
            }

            if (this.collisionCounter >= 8) {
                this.destroyBlocksInAABB(this.getBoundingBox());
                this.collisionCounter = 0;
            }
        } else {
            if (this.isRampaging()) {
                float rotation = this.tickCount / 8F;
                for (int i = 0; i < 15; i++) {
                    addSnowEffect(rotation + (i * 30), i + rotation);
                }
                this.walkAnimation.setSpeed(this.walkAnimation.speed() + 0.4F);
            }
        }
    }

    private void addSnowEffect(float rotation, float hgt) {
        double px = 2F * Math.cos(rotation);
        double py = hgt % 3F;
        double pz = 2F * Math.sin(rotation);
        this.level().addParticle(TFParticleType.SNOW.get(), this.xOld + px, this.yOld + py, this.zOld + pz, 0, 0, 0);
    }

    public void destroyBlocksInAABB(AABB box) {
        if (ForgeEventFactory.getMobGriefingEvent(this.level(), this)) {
            for (BlockPos pos : WorldUtil.getAllInBB(box)) {
                if (EntityUtil.canDestroyBlock(this.level(), pos, this)) {
                    this.level().destroyBlock(pos, false);
                }
            }
        }
    }

    public void makeRandomBlockFall(int range, int hangTime) {
        if (ForgeEventFactory.getMobGriefingEvent(this.level(), this)) {
            int bx = Mth.floor(this.getX()) + this.getRandom().nextInt(range) - this.getRandom().nextInt(range);
            int bz = Mth.floor(this.getZ()) + this.getRandom().nextInt(range) - this.getRandom().nextInt(range);
            int by = Mth.floor(this.getY() + this.getEyeHeight());
            
            this.makeBlockFallAbove(new BlockPos(bx, by, bz), hangTime);
        }
    }

    private void makeBlockFallAbove(BlockPos pos, int hangTime) {
        for (int i = 1; i < 15; i++) {
            BlockPos up = pos.above(i);
            if (this.level().getBlockState(up).is(BlockTags.ICE) && this.level().getBlockState(up.below()).isAir()) {
                this.makeBlockFall(up, hangTime);
                break;
            }
        }
    }

    public void makeBlockAboveTargetFall() {
        if (this.getTarget() != null) {
            this.makeBlockFallAbove(this.getTarget().blockPosition(), 30);
        }
    }

    private void makeBlockFall(BlockPos pos, int hangTime) {
        FallingIce ice = new FallingIce(this.level(), pos.getX() + 0.5D, pos.getY(), pos.getZ() + 0.5D,
        this.level().getBlockState(pos), hangTime);
        this.level().setBlockAndUpdate(pos, Blocks.AIR.defaultBlockState());
        this.level().addFreshEntity(ice);
    }

    @Override
    public boolean hurt(DamageSource source, float amount) {
        if (source.getDirectEntity() instanceof IceBomb iceBomb && iceBomb.getOwner() == this) {
            return false;
        }

        if (source.getEntity() instanceof IceBomb iceBomb && iceBomb.getOwner() == this) {
            return false;
        }

        if (source.getDirectEntity() instanceof FallingIce) {
            return false;
        }

        if (source.is(net.minecraft.tags.DamageTypeTags.IS_FREEZING)) {
            return false;
        }

        if (source.is(twilightforest.init.TFDamageTypes.FROZEN)) {
            return false;
        }

        if (source.getDirectEntity() instanceof IceBomb || source.getEntity() instanceof IceBomb) {
            if (source.getDirectEntity() instanceof IceBomb iceBomb && iceBomb.getOwner() == this) {
                return false;
            }
            if (source.getEntity() instanceof IceBomb iceBomb && iceBomb.getOwner() == this) {
                return false;
            }
            if (this.isTame() && source.getDirectEntity() instanceof IceBomb iceBomb &&
                iceBomb.getOwner() instanceof MiniYetiEntity otherYeti &&
                otherYeti.isTame() && otherYeti.getOwner() == this.getOwner()) {
                return false;
            }
        }

        if (this.isTame() && source.getEntity() == this.getOwner()) {
            return false;
        }

        if (this.isTame() && source.getEntity() instanceof MiniYetiEntity otherYeti &&
            otherYeti.isTame() && otherYeti.getOwner() == this.getOwner()) {
            return false;
        }

        if (!this.canRampage && source.is(net.minecraft.tags.DamageTypeTags.IS_PROJECTILE)) {
            amount *= 0.3F;
        }

        this.canRampage = true;
        return super.hurt(source, amount);
    }

    @Override
    public void tick() {
        super.tick();
        if (this.hasEffect(twilightforest.init.TFMobEffects.FROSTY.get())) {
            this.removeEffect(twilightforest.init.TFMobEffects.FROSTY.get());
        }
    }

    @Override
    public void positionRider(Entity passenger, Entity.MoveFunction callback) {
        Vec3 riderPos = this.getRiderPosition();
        callback.accept(passenger, riderPos.x(), riderPos.y(), riderPos.z());
    }

    @Override
    public double getPassengersRidingOffset() {
        return 2.5D;
    }

    private Vec3 getRiderPosition() {
        if (this.isVehicle()) {
            float distance = 0.3F;
            double dx = Math.cos((this.getYRot() + 90) * Math.PI / 180.0D) * distance;
            double dz = Math.sin((this.getYRot() + 90) * Math.PI / 180.0D) * distance;
            return new Vec3(this.getX() + dx,
            this.getY() + this.getPassengersRidingOffset() + this.getPassengers().get(0).getMyRidingOffset(),
            this.getZ() + dz);
        } else {
            return new Vec3(this.getX(), this.getY(), this.getZ());
        }
    }

    @Override
    public boolean canRiderInteract() {
        return true;
    }

    @Override
    public void performRangedAttack(LivingEntity target, float distanceFactor) {
        twilight.companion.entity.projectile.MiniYetiIceBomb ice = new twilight.companion.entity.projectile.MiniYetiIceBomb(
        TFEntities.THROWN_ICE.get(), this.level(), this);

        Vec3 targetVelocity = target.getDeltaMovement();
        double distance = this.distanceTo(target);
        double timeToTarget = distance / 1.2F;

        double predictedX = target.getX() + targetVelocity.x * timeToTarget;
        double predictedY = target.getBoundingBox().minY + target.getBbHeight() / 2.0F;
        double predictedZ = target.getZ() + targetVelocity.z * timeToTarget;

        double d0 = predictedX - this.getX();
        double d1 = predictedY - ice.getY();
        double d2 = predictedZ - this.getZ();
        double d3 = Mth.sqrt((float) (d0 * d0 + d2 * d2));

        ice.shoot(d0, d1 + d3 * 0.1D, d2, 1.4F, 2.0F);

        this.gameEvent(GameEvent.PROJECTILE_SHOOT);
        this.level().addFreshEntity(ice);
        this.alertNearbyAllies(target);
    }

    private void alertNearbyAllies(LivingEntity target) {
        if (this.getOwner() != null) {
            for (MiniYetiEntity ally : this.level().getEntitiesOfClass(MiniYetiEntity.class,
                this.getBoundingBox().inflate(16.0D))) {
                if (ally != this && ally.isTame() && ally.getOwner() == this.getOwner() && ally.getTarget() == null) {
                    ally.setTarget(target);
                }
            }
        }
    }

    public boolean canRampage() {
        return this.canRampage;
    }

    public void setRampaging(boolean rampaging) {
        getEntityData().set(RAMPAGE_FLAG, (byte) (rampaging ? 1 : 0));
    }

    public boolean isRampaging() {
        return getEntityData().get(RAMPAGE_FLAG) == 1;
    }

    public void setGiant(boolean giant) {
        this.getEntityData().set(GIANT_FLAG, (byte) (giant ? 1 : 0));
        if (giant && !this.isSummoning()) {
            this.targetScale = 1.2F;
            this.growthTicks = 100;
        }
        this.refreshDimensions();
        this.setBoundingBox(this.makeBoundingBox());
    }

    @Override
    public net.minecraft.world.entity.EntityDimensions getDimensions(net.minecraft.world.entity.Pose pose) {
        float baseWidth = 3.8F;
        float baseHeight = 5.5F;

        float currentScale = this.getGrowthScale();
        return net.minecraft.world.entity.EntityDimensions.scalable(baseWidth * currentScale,
        baseHeight * currentScale);
    }

    public float getGrowthScale() {
        return this.getEntityData().get(GROWTH_SCALE);
    }

    public void setGrowthScale(float scale) {
        this.getEntityData().set(GROWTH_SCALE, scale);
        this.refreshDimensions();
        this.setBoundingBox(this.makeBoundingBox());
    }

    public boolean isGiant() {
        return this.getEntityData().get(GIANT_FLAG) == 1;
    }

    public void setCrouching(boolean crouching) {
        this.getEntityData().set(CROUCHING_FLAG, (byte) (crouching ? 1 : 0));
    }

    public boolean isCrouching() {
        return this.getEntityData().get(CROUCHING_FLAG) == 1;
    }

    public void startSummonAnimation() {
        this.getEntityData().set(SUMMON_ANIMATION, true);
        this.getEntityData().set(SUMMON_TIMER, 0);
        this.setGrowthScale(0.01F);
    }

    public boolean isSummoning() {
        return this.getEntityData().get(SUMMON_ANIMATION);
    }

    public void setGrowthTicks(int ticks) {
        this.growthTicks = ticks;
    }

    public void setTargetScale(float scale) {
        this.targetScale = scale;
    }

    public void setGiantWithoutAnimation(boolean giant) {
    this.getEntityData().set(GIANT_FLAG, (byte) (giant ? 1 : 0));
        if (giant) {
            this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(400.0D);
            this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(30.0D);
            this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.25D);
            this.getAttribute(Attributes.KNOCKBACK_RESISTANCE).setBaseValue(0.8D);
        }
        this.refreshDimensions();
        this.setBoundingBox(this.makeBoundingBox());
    }

    @Override
    public boolean causeFallDamage(float distance, float multiplier, DamageSource source) {
        if (!this.level().isClientSide() && isRampaging()) {
            this.hitNearbyEntities();
        }
        return super.causeFallDamage(distance, multiplier, source);
    }

    private void hitNearbyEntities() {
        for (LivingEntity entity : this.level().getEntitiesOfClass(LivingEntity.class,
            this.getBoundingBox().inflate(3, 0, 3))) {
            if (entity != this && !isOwnerOrAlly(entity) && entity.hurt(this.damageSources().mobAttack(this), 3F)) {
                entity.push(0, 0.3, 0);
            }
        }
    }

    private boolean isOwnerOrAlly(LivingEntity entity) {
        if (this.isTame() && this.getOwner() != null) {
            if (entity == this.getOwner()) {
                return true;
            }
            if (entity instanceof MiniYetiEntity otherYeti && otherYeti.isTame()) {
                return otherYeti.getOwner() == this.getOwner();
            }
        }
        return false;
    }

    @Override
    public void addAdditionalSaveData(CompoundTag compound) {
        super.addAdditionalSaveData(compound);
        compound.putBoolean("CanRampage", this.canRampage);
        compound.putBoolean("IsGiant", this.isGiant());
        compound.putBoolean("IsCrouching", this.isCrouching());
        compound.putFloat("GrowthScale", this.getGrowthScale());
        compound.putFloat("TargetScale", this.targetScale);
        compound.putInt("GrowthTicks", this.growthTicks);
        compound.putBoolean("SummonAnimation", this.getEntityData().get(SUMMON_ANIMATION));
        compound.putInt("SummonTimer", this.getEntityData().get(SUMMON_TIMER));
    }

    @Override
    public void readAdditionalSaveData(CompoundTag compound) {
        super.readAdditionalSaveData(compound);
        this.canRampage = compound.getBoolean("CanRampage");

        if (compound.contains("IsCrouching")) {
            this.setCrouching(compound.getBoolean("IsCrouching"));
        }
        if (compound.contains("GrowthScale")) {
            this.setGrowthScale(compound.getFloat("GrowthScale"));
        }
        if (compound.contains("TargetScale")) {
            this.targetScale = compound.getFloat("TargetScale");
        }
        if (compound.contains("GrowthTicks")) {
            this.growthTicks = compound.getInt("GrowthTicks");
        }
        if (compound.contains("SummonAnimation")) {
            this.getEntityData().set(SUMMON_ANIMATION, compound.getBoolean("SummonAnimation"));
        }
        if (compound.contains("SummonTimer")) {
            this.getEntityData().set(SUMMON_TIMER, compound.getInt("SummonTimer"));
        }

        if (compound.getBoolean("IsGiant")) {
            this.getEntityData().set(GIANT_FLAG, (byte) 1);
            if (this.getGrowthScale() >= 1.2F && this.growthTicks <= 0) {
                this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(400.0D);
                this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(30.0D);
                this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.25D);
                this.getAttribute(Attributes.KNOCKBACK_RESISTANCE).setBaseValue(0.8D);
            }
        }
    }

    @Override
    protected boolean canRide(Entity entityIn) {
        return false;
    }

    @Override
    public InteractionResult mobInteract(Player player, InteractionHand hand) {
        ItemStack itemstack = player.getItemInHand(hand);
        if (itemstack.getItem() instanceof twilight.companion.item.TwilightContractItem) {
            if (this.isTame() && this.isOwnedBy(player)) {
                if (!itemstack.hasTag() || !itemstack.getTag().contains("contract_type")) {
                    if (!this.level().isClientSide()) {
                        var tag = itemstack.getOrCreateTag();
                        tag.putString("contract_type", "mini_yeti");

                        var yetiData = new net.minecraft.nbt.CompoundTag();
                        yetiData.putBoolean("is_giant", this.isGiant());
                        yetiData.putFloat("health", this.getHealth());
                        yetiData.putBoolean("is_crouching", this.isCrouching());
                        tag.put("yeti_data", yetiData);

                        this.playSound(SoundEvents.ITEM_PICKUP, 1.0F, 1.0F);
                        this.getEntityData().set(CAPTURE_ANIMATION, true);
                        this.getEntityData().set(CAPTURE_TIMER, 0);
                    }
                    return InteractionResult.SUCCESS;
                }
            }
            return InteractionResult.PASS;
        }

        if (itemstack.is(Items.ICE)) {
            if (!this.level().isClientSide()) {
                if (!player.getAbilities().instabuild) {
                    itemstack.shrink(1);
                }

                if (!this.isGiant()) {
                    this.setGiant(true);
                    this.playSound(TFSounds.ALPHA_YETI_GROWL.get(), 3.0F, 0.5F);
                    for (int i = 0; i < 20; i++) {
                        this.level().addParticle(ParticleTypes.SNOWFLAKE,
                        this.getX() + (this.random.nextDouble() - 0.5D) * 4.0D,
                        this.getY() + this.random.nextDouble() * 3.0D,
                        this.getZ() + (this.random.nextDouble() - 0.5D) * 4.0D,
                        0.0D, 0.1D, 0.0D);
                    }
                } else {
                    float maxHealth = this.getMaxHealth();
                    float healAmount = maxHealth * 0.1F;
                    float currentHealth = this.getHealth();

                    if (currentHealth < maxHealth) {
                        this.setHealth(Math.min(currentHealth + healAmount, maxHealth));
                        this.playSound(SoundEvents.PLAYER_LEVELUP, 1.0F, 1.5F);
                        for (int i = 0; i < 15; i++) {
                            this.level().addParticle(ParticleTypes.HEART,
                            this.getX() + (this.random.nextDouble() - 0.5D) * 3.0D,
                            this.getY() + this.random.nextDouble() * 3.0D + 1.0D,
                            this.getZ() + (this.random.nextDouble() - 0.5D) * 3.0D,
                            0.0D, 0.1D, 0.0D);
                        }
                    }
                }
            }
            return InteractionResult.SUCCESS;
        }

        if (itemstack.isEmpty() && this.isTame() && this.isOwnedBy(player)) {
            if (player.isShiftKeyDown() && !this.level().isClientSide()) {
                this.setCrouching(!this.isCrouching());
                return InteractionResult.SUCCESS;
            }
        }

        if (this.isTame()) {
            if (this.isOwnedBy(player)) {
                if (player.isShiftKeyDown() && !this.level().isClientSide()) {
                    this.setOrderedToSit(!this.isOrderedToSit());
                    return InteractionResult.SUCCESS;
                }
                return InteractionResult.SUCCESS;
            }
        }
        return super.mobInteract(player, hand);
    }

    @Override
    public boolean canAttack(LivingEntity target) {
        if (this.isTame()) {
            if (target == this.getOwner()) {
                return false;
            }
            if (target instanceof MiniYetiEntity otherYeti && otherYeti.isTame() &&
                otherYeti.getOwner() == this.getOwner()) {
                return false;
            }
            if (target instanceof Player player && this.getOwner() instanceof Player owner) {
                if (owner.getLastHurtMob() != player) {
                    return false;
                }
            }
            return super.canAttack(target);
        }
        return super.canAttack(target);
    }

    @Override
    public void setTarget(@Nullable LivingEntity target) {
        if (target != null && target != this.getTarget()) {
            this.playSound(TFSounds.ALPHA_YETI_ALERT.get(), 2F, 0.8F + this.getRandom().nextFloat() * 0.4F);
        }
        super.setTarget(target);
        if (target != null && this.isTame()) {
            this.alertNearbyAllies(target);
        }
    }

    @Nullable
    @Override
    protected SoundEvent getAmbientSound() {
        return TFSounds.ALPHA_YETI_GROWL.get();
    }

    @Override
    protected SoundEvent getHurtSound(DamageSource source) {
        return TFSounds.ALPHA_YETI_HURT.get();
    }

    @Override
    protected SoundEvent getDeathSound() {
        return TFSounds.ALPHA_YETI_DEATH.get();
    }

    @Override
    public float getVoicePitch() {
        return 0.8F + this.getRandom().nextFloat() * 0.4F;
    }

    @Override
    protected float getSoundVolume() {
        return 2.0F;
    }

    @Override
    public void lavaHurt() {
        if (!this.fireImmune()) {
            this.setSecondsOnFire(3);
            if (this.hurt(this.damageSources().lava(), 2F)) {
                this.playSound(SoundEvents.GENERIC_BURN, 0.4F, 2.0F + this.random.nextFloat() * 0.4F);
                EntityUtil.killLavaAround(this);
            }
        }
    }

    @Override
    public boolean canChangeDimensions() {
        return false;
    }
}